package com.zsd.android.okhttplib;

import android.support.annotation.NonNull;

import com.zsd.android.okhttplib.bean.HttpConnection;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.concurrent.Executor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ConnectionPool {

    /**
     * 最大允许闲置的时间
     * 检查机制
     *
     * 每个一分钟就去检查线程池里面的链接是否可用,如果不可用就移除
     */
    private long keepAlive;
    private boolean clearRunnableFlag;
    /**
     * 队列，专门存放连接对象
     * 连接池的存放容器
     */
    private Deque<HttpConnection> httpConnectionDeque;
    //线程池
    private Executor threadPoolExecutor;

    public ConnectionPool() {
        this(1, TimeUnit.MINUTES);
    }

    public ConnectionPool(long keepAlive, TimeUnit timeUnit) {
        this.keepAlive = timeUnit.toMillis(keepAlive);
        httpConnectionDeque = new ArrayDeque<>();
        threadPoolExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(),
                new ThreadFactory() {
            @Override
            public Thread newThread(@NonNull Runnable r) {
                Thread thread = new Thread(r, "ConnectionPool");
                thread.setDaemon(false);
                return thread;
            }
        });
    }

    /**
     * 开启一个线程专门去检查连接池里面的链接对象
     * 清理连接池里面的链接对象
     */
    private Runnable clearRunnable = new Runnable() {
        @Override
        public void run() {
            while (true) {
                //当前的时间
                long nextCheckCleanTime = clean(System.currentTimeMillis());
                if (-1 == nextCheckCleanTime) {
                    return;
                }
                if (nextCheckCleanTime > 0) {
                    //等待一段时间后，再去检查是否要清理
                    synchronized (ConnectionPool.this) {
                        try {
                            ConnectionPool.this.wait(nextCheckCleanTime);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    };

    /**
     * 清理那先链接对象
     * @param currentTimeMillis
     * @return
     */
    private long clean(long currentTimeMillis) {
        long idleRecordSave = -1;
        Iterator<HttpConnection> iterator = httpConnectionDeque.iterator();
        while (iterator.hasNext()) {
            HttpConnection httpConnection = iterator.next();
            //todo 我们添加了一个连接对象，超过了（最大闲置时间）就会移除这个链接对象
            //计算出来的闲置时间
            long idleTime = currentTimeMillis - httpConnection.getLastUseTime();
            if (idleTime > keepAlive) {
                //大于最大允许的闲置时间
                //移除
                iterator.remove();
                //关闭socket，因为socket一直保持连接的话很好内存
                httpConnection.closeSocket();
                continue;
            }
            //得到最长的闲置时间（计算）
            if (idleRecordSave < idleTime) {
                idleRecordSave = idleTime;
            }
        }
        //出来循环之后，idleRecordSave值计算完毕
        //keepAlive=60s idleRecordSave = 30 60 - 30
        if (idleRecordSave >= 0) {
            return (keepAlive - idleRecordSave);
        }
        //没有计算好，连接池里面没有连接对象，结束掉线程池中的任务
        return idleRecordSave;
    }

    /**
     * todo 添加连接对象 ----> 连接池里面
     * @param httpConnection
     */
    public synchronized void putConnection(HttpConnection httpConnection) {
        //一旦put的时候就要去检查连接池里面是否要去清理
        if (!clearRunnableFlag) {
            //如果没有执行就去执行
            clearRunnableFlag = true;
            threadPoolExecutor.execute(clearRunnable);
        }
        httpConnectionDeque.add(httpConnection);
    }

    /**
     * todo 获取连接对象 ----> 连接池里面可用的有效的（复用）
     * 通过域名和端口号查找有效的链接对象
     * @param host 域名
     * @param port 端口号
     * @return
     */
    public HttpConnection getConnection(String host, int port) {
        Iterator<HttpConnection> iterator = httpConnectionDeque.iterator();
        while (iterator.hasNext()) {
            HttpConnection httpConnection = iterator.next();
            if (httpConnection.isConnectionAction(host, port)) {
                //移除
                iterator.remove();
                //代表我们找到了，可以复用
                return httpConnection;
            }
        }
        return null;
    }
}
